package
{
	import com.bit101.components.InputText;
	import com.bit101.components.Label;
	import com.bit101.components.PushButton;
	import com.bit101.components.Text;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.system.Capabilities;
	import flash.utils.Timer;
	import flash.utils.getTimer;
	
	//[SWF(width="465", height="465", backgroundColor="#f0f0f0", frameRate=30)]

	public class PiCalculator extends Sprite
	{
		private var _ps:PiSpigot;
		private var _psg:PiSpigotGibbons;
		private var _txt_result_pi:Text;
		private var _btn_calc:PushButton;
		private var _btn_bench:PushButton;
		private var _it:InputText;
		private var _lbl_title:Label;
		private var _lbl_digits:Label;
		private var _lbl_result:Label;
		
		private var _bench_cnt:int;
		private var _ary_time:Array;
		private var _flg_bench:Boolean;
		private var _digits:int;
		private var _time_start:Number;
		private var _time_end:Number;
		private var _interval_timer:Timer;
		
		private const DIGITS_DEFAULT:int = 3000;
		private const BENCH_TRY_NUM:int = 5;
		private const DIGITS_MAX_LIMIT:int = 15000;

		public function PiCalculator()
		{
			_lbl_title = new Label(this, 10, 5, "Pi Calculator (Spigot Algorithms)");
			
			_txt_result_pi = new Text(this, 10, 25);
			_txt_result_pi.width = 445;
			_txt_result_pi.height = 315;
			_txt_result_pi.editable = true;
			
			_it = new InputText(this, 20, 350, DIGITS_DEFAULT.toString());
			_it.width = 60;
			_it.height = 20;
			
			_lbl_digits = new Label(this, 80, 350, "digits");

			_btn_calc = new PushButton(this, 120, 350, "Calc", onButtonCalcClickHandler);
			_btn_calc.width = 50;
			
			_btn_bench = new PushButton(this, 175, 350, "Bench", onButtonBenchClickHandler);
			_btn_bench.width = 50;

			_lbl_result = new Label(this, 10, 380, "");
			
			if( flash.system.Capabilities.isDebugger )
				_lbl_result.text = "use debug player.";
			
			_ps = new PiSpigot();
			_psg = new PiSpigotGibbons();
		}
		
		private function start():void
		{
			var digits:Number = Number(_it.text);
			
			if (digits < 4 || isNaN(digits))
				digits = DIGITS_DEFAULT;
			else if( digits > DIGITS_MAX_LIMIT )
				digits = DIGITS_MAX_LIMIT;
				
			_ary_time = [];
			_digits = digits;
			_bench_cnt = 0;
				
			_txt_result_pi.text = "";
			_it.text = _digits.toString();
			_lbl_result.text = "";
			
			if( !_interval_timer )
			{
				_interval_timer = new Timer( 500, 1 );
				_interval_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerCompleteHandler);
			}
			
			_interval_timer.reset();
			_interval_timer.start();
		}

		private function onButtonCalcClickHandler(event:Event):void
		{
			_flg_bench = false;
			start();
		}
		
		private function onButtonBenchClickHandler(event:Event):void
		{
			_flg_bench = true;
			start();
		}
		
		private function onTimerCompleteHandler(event:TimerEvent):void
		{
			var time_start:int;
			var pass_time:int;
			var result:String;
			
			time_start = getTimer();
			
			_ps.init( _digits );
			//_psg.init( _digits );
			
			result = _ps.calc();
			//result = _psg.calc();
			
			pass_time = getTimer() - time_start;
			
			_ary_time.push( pass_time );
			
			_lbl_result.text += String(pass_time) + " ms\n";
			
			if( !_flg_bench || ++_bench_cnt >= BENCH_TRY_NUM)
				end(result);
			else
				_interval_timer.start();
		}
		
		private function end(a_result_pi:String):void
		{
			var tmp_time:Number = 0;
			
			_txt_result_pi.text = a_result_pi.charAt(0) + "." + a_result_pi.substr(1);
			
			if( !_flg_bench )
				return;
			
			for each( var time:int in _ary_time )
				tmp_time += time;
				
			_lbl_result.text += "avg. " + (int((tmp_time / _ary_time.length) * 1000) / 1000) + " ms";
		}
		
	}

}

class PiSpigot
{
	//private var _digits:int;
	private var _a:Array;
	private var _b:Number, _c:Number, _d:Number, _e:Number, _f:Number, _g:Number, _h:Number;

	public function PiSpigot()
	{
	}
	
	public function init(a_digits:uint):void
	{
		//_digits = a_digits;

		_a = [];
		_b = _d = _e = _g = _h = 0;
		_c = (a_digits / 4 + 1) * 14;
		_f = 10000;
	}

	public function calc():String
	{
		var ret_str:String = "";
		var tmp_str:String;
		var i:int;
		
		while((_b = _c -= 14) > 0)
		{
			for (_e = _d %= _f; _g = --_b * 2; _d = int(_d / _g))
			{
				_d = _d * _b + _f * (_h ? _a[_b] : _f / 5);
				_a[_b] = _d % --_g;
			}
	
			_h = _e + int(_d / _f);
			
			//ret_str += ("0000" + _h).substr(-4);
			
			tmp_str = _h.toString();
			
			for ( i = tmp_str.length; i < 4; i++)
				tmp_str = '0' + tmp_str;
				
			ret_str += tmp_str;
		}
		
		return ret_str;
	}

}

class PiSpigotGibbons
{
	private var _tc:int;
	private var _q:Number, _r:Number, _t:Number, _i:Number, _u:Number, _y:Number;

	public function PiSpigotGibbons()
	{
	}
	
	public function init(a_digits:int):void
	{
		//_digits = a_digits;
		
		_tc = a_digits;
		
		_q = 1;
		_r = 180;
		_t = 60;
		_i = 2;
		_u = 3 * (3 * _i + 1) * (3 * _i + 2);
		_y = int((_q * (27 * _i - 12) + 5 * _r) / 5 / _t );
	}

	public function calc():String
	{
		var ret_str:String = String(_y);
		var q:Number, r:Number;
		
		while(_tc--)
		{
			q = 10 * _q * _i * (2 * _i - 1);
			r = 10 * _u * (_q * (5 * _i - 2) + _r - _y * _t);
			_t = _t * _u;
			_i++;
			_u = 3 * (3 * _i + 1) * (3 * _i + 2);
			_y = int((q * (27 * _i - 12) + 5 * r) / 5 / _t);
				
			ret_str += String(_y);
			
			_q = q;
			_r = r;
		}
		
		return ret_str;
	}

}